package com.uxsino.simo.indicator.retractor;

import com.fasterxml.jackson.annotation.JsonProperty;
import com.google.common.primitives.Longs;
import com.uxsino.commons.moment.Moments;
import com.uxsino.commons.utils.config.ConfigProp;
import com.uxsino.reactorq.model.INDICATOR_TYPE;
import com.uxsino.simo.networkentity.EntityInfo;
import com.uxsino.simo.query.QueryContext;
import com.uxsino.simo.query.QueryTemplate;
import org.mvel2.MVEL;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.time.format.DateTimeFormatter;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * The return value of the overridden abstract method {@code retractHelper} may be the one of following:
 * 
 * An instance of {@link Map}, from the subclasses of {@link CompoundValueRetractor} or {@link DatasetKVRetractor}
 *  
 * An instance of {@link List} of {@link Map}s, from the subclasses of {@link ListValueRetractor}
 * 
 * An instance of {@link String}, from the subclasses of {@link SimpleValueRetractor}
 * 
 * The exposed interface method {@code retract} calls the {@code retractHelper} method and
 * adds the parameter values to the returned {@link Map} object, or each of the {@link Map} object 
 * in the returned {@link List} object. At the moment, it does nothing to the returned
 * {@link String} object.
 * 
 * The method that consumes the retracted value should then just take the necessary components
 * from the returned object and perform its actions.
 * 
 * 
 *
 * @param <ReturnType>
 */
public abstract class AbstractValueRetractor<ReturnType> implements IValueRetractor,Transformer {
     private static Logger logger = LoggerFactory.getLogger(AbstractValueRetractor.class);

    @JsonProperty
    protected INDICATOR_TYPE valueType;

    @ConfigProp(name = "transform")
    public String transformer;

    @ConfigProp(name = "transform-origin")
    public String transformerOrigin;

    public static class FN {
        public static FN of(){
            return new FN();
        }

        /**
         * 进制转换
         * @param src
         * @param format pos mark
         * @param radix   the radix to be used while parsing {@code s}.
         * <hr/>
         * <br/>eg:
         *        src is: "07E30C09"<br/>
         *        format: "0-4,-,4-6,-,6-8"<br/>
         *        return: "2019-12-9"<br/>
         * @return
         */
        public String parse(String src, String format, int radix){
            if(src == null){
                return null;
            }
            StringBuffer res = new StringBuffer();

            Arrays.asList(format.split(",")).stream().forEach(seg->{
                String[] idxs = seg.split("-");
                if(idxs.length == 2){
                    Long s = Longs.tryParse(idxs[0]);
                    Long e = Longs.tryParse(idxs[1]);
                    if("$".equals(idxs[1])){
                        e = src.length()*1L;
                    }
                    if(e == null || s == null){
                        logger.error("radix convert error with illegal expression：{} \n  value: {}", format, src);
                        return;
                    }
                    if(s.intValue() > src.length() || e.intValue() > src.length()){
                        logger.error("radix convert error with IndexOutOfBoundsException expression：{} \n  value: {}", format, src);
                        return;
                    }
                    String itm = src.substring(s.intValue(), e.intValue());
                    res.append(Longs.tryParse(itm, radix));
                }else{
                    res.append(seg);
                }
            });

            return res.toString();
        }

        /**
         *
         * @param srcFormat 原格式
         * @param destFormat 目标格式
         * @param value     元数据
         * @return
         */
        public String time(String srcFormat, String destFormat, String value){
            return Moments.from(value, srcFormat).format(destFormat);
        }

        /*public static void main(String[] args) {
            String res  = FN.of().time("yyyy-MM-dd'T'HH:mm:ss", "yyyy-MM-dd HH:mm:ss", "2020-01-02T15:03:07.596+08:00");
            System.out.println(res);
        }*/
    }

//    @ConfigProp(name = "cast_formula")
//    public String cast;

    private final Object trans(String transformerExpression, Object s){
        if(transformerExpression == null){
            return s;
        }
        HashMap<String, Object> variables = new HashMap<>();
        variables.put("value", s);
        variables.put("FN", FN.of());

        try {
            Object r = MVEL.eval(transformerExpression, variables);
            if (r != null)
                return r;
        } catch (Exception e) {
            logger.error("error evaluating transformer: {}, value=\"{}\"", transformerExpression, s);
        }
        return null;
    }

    @Override
    public Object transForm(Object s) {
        return trans(this.transformer, s);
    }

    abstract Object doRetract(EntityInfo entity, QueryContext ctxt, QueryTemplate qt, Object obj);

    @Override
    public final Object retract(EntityInfo entity, QueryContext ctxt, QueryTemplate qt, Object obj){
        obj = transFormOrigin(obj);
        obj = this.doRetract(entity, ctxt, qt, obj);
        postRetract(entity, ctxt, qt, obj);
        return transForm(obj);
    }

    public Object transFormOrigin(Object s){
        return trans(this.transformerOrigin, s);
    }

//    /**
//     * 所有数据处理完成后，做数据类型转换或者数据处理
//     * @param obj
//     * @return
//     */
//    @Override
//    public Object cast(Object obj) {
//        if(Strings.isNullOrEmpty(cast)){
//            return obj;
//        } else {
//            Map<String, Object> p = Maps.newHashMap();
//            p.put("value", obj);
//          return MVEL.eval(this.cast, p);
//        }
//    }

    public AbstractValueRetractor(INDICATOR_TYPE valueType) {
        this.valueType = valueType;
    }

    @Override
    public final INDICATOR_TYPE getValueType() {
        return valueType;
    }
}
